<?php
/**
 * Idea Framework (http://www.ideaphp.net/)
 *
 * @author    Huan Song <ideaphp@gmail.com>
 * @link      http://www.ideaphp.net/
 * @copyright Copyright (c) 2011-2013 http://www.ideaphp.net/ All rights reserved.
 * @license   http://www.apache.org/licenses/LICENSE-2.0
 */

namespace idea\saf\sl;

use idea\ap\Singleton;
use idea\mvc\Html;
use idea\saf\Log;

/**
 * Form abstract class file
 * 表单管理基类
 * @author 宋欢 <ideaphp@gmail.com>
 * @version $Id: Form.php 1 2013-05-18 14:58:59Z huan.song $
 * @package idea.saf.sl
 * @since 1.0
 */
abstract class Form
{
    /**
     * @var instance of idea\mvc\Html
     */
    protected $_html = null;

    /**
     * @var array 寄存验证规则
     */
    protected $_rules = null;

    /**
     * 获取验证规则，如果指定$name，只获取指定的验证规则
     * @param string|null $name
     * @return array
     */
    public function getRules($name = null)
    {
        if ($this->_rules === null) {
            $this->_rules = array();
            $attributes = $this->getAttributes();
            foreach ($attributes as $columnName => $attribute) {
                if (!isset($attribute['rule'])) {
                    continue;
                }

                $this->_rules[$columnName] = $attribute['rule'];
            }
        }

        if ($name === null) {
            return $this->_rules;
        }

        if (isset($this->_rules[$name])) {
            return $this->_rules[$name];
        }

        Log::warning(sprintf(
            'Form no rule is registered for name "%s", _rules "%s".', $name, serialize($this->_rules)
        ));
        return array();
    }

    /**
     * 通过表单名，获取表单属性
     * @param string $name
     * @return array
     */
    public function getAttributes($name = null)
    {
        if (!isset($this->_attributes)) {
            Log::warning(sprintf(
                'Form Property "%s.%s" was not defined.', get_class($this), '_attributes'
            ));

            return array();
        }

        if (!is_array($this->_attributes)) {
            Log::warning(sprintf(
                'Form Property "%s.%s" must be array.', get_class($this), '_attributes'
            ));

            return array();
        }

        if ($name === null) {
            return $this->_attributes;
        }

        if (isset($this->_attributes[$name])) {
            return $this->_attributes[$name];
        }

        Log::warning(sprintf(
            'Form no attribute is registered for name "%s", _attributes "%s".', $name, serialize($this->_attributes)
        ));
        return array();
    }

    /**
     * 通过表单名和获取选项的方法，获取含有多个选项的表单元素
     * @param string $name
     * @param integer $chkPos
     * @return array
     * @throws ErrorException 如果获取选项的方法不存在，抛出异常
     */
    public function getOptionsByName($name, $chkPos = null)
    {
        $name = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($name))));
        $method = 'get' . $name . 'Options';
        if (method_exists($this, $method)) {
            if ($chkPos !== null) {
                return $this->$method($chkPos);
            }
            else {
                return $this->$method();
            }
        }

        Log::warning(sprintf(
            'Form Property "%s.%s" was not defined.', get_class($this), $method
        ));
        return array();
    }

    /**
     * 获取含有多个选项的表单元素，根据需求将选中默认被选择的项
     * @param array $data
     * @param integer $chkPos
     * @return array
     */
    public function getOptions(array $data = array(), $chkPos = 0)
    {
        $options = array();
        $pos = 0;
        foreach ($data as $value => $label) {
            $options[] = array(
                'value' => $value,
                'label' => $label,
                'checked' => ($pos++ === $chkPos) ? true : false,
            );
        }

        return $options;
    }

    /**
     * 获取表单元素：<input type="text" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function text($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->text($name, $value, $attributes);
    }

    /**
     * 获取表单元素：<input type="password" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function password($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->password($name, $value, $attributes);
    }

    /**
     * 获取表单元素：<input type="hidden" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function hidden($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->hidden($name, $value, $attributes);
    }

    /**
     * 获取表单元素：<input type="file" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function file($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->file($name, $value, $attributes);
    }

    /**
     * 获取表单元素：<input type="submit" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function submit($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->submit($value, $name, $attributes);
    }

    /**
     * 获取表单元素：<input type="reset" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function reset($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->reset($value, $name, $attributes);
    }

    /**
     * 获取表单元素：<input type="image" src="" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function image($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->image($value, $name, $attributes);
    }

    /**
     * 获取表单元素：<input type="button" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function button($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->button($value, $name, $attributes);
    }

    /**
     * 获取表单元素：<textarea></textarea>
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function textarea($name, $value, $attributes, array $options = array())
    {
        $encode = isset($attributes['encode']) ? (boolean) $attributes['encode'] : true;
        unset($attributes['encode']);
        return $this->getHtml()->textarea($name, $value, $attributes, $encode);
    }

    /**
     * 获取表单元素：<img src="" alt="" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function img($name, $value, $attributes, array $options = array())
    {
        return $this->getHtml()->img($value, $name, $attributes);
    }

    /**
     * 获取表单元素：<select><option name=""></option></select>
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function select($name, $value, $attributes, array $options = array())
    {
        $html = $this->getHtml()->openSelect($name, $attributes) . "\n";
        if (empty($options)) {
            $options = $this->getOptionsByName($name);
        }
        
        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }

            if (!isset($option['value'])) {
                continue;
            }

            $checked = isset($option['checked']) ? (boolean) $option['checked'] : false;
            if ($option['value'] === $value) {
                $checked = true;
            }

            $html .= $this->getHtml()->option($option['label'], $option['value'], $checked) . "\n";
        }

        $html .= $this->getHtml()->closeSelect() . "\n";
        return $html;
    }

    /**
     * 获取表单元素：<input type="radio" /><input type="radio" /><input type="radio" />
     * @param string $name
     * @param mixed $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function radio($name, $value, $attributes, array $options = array())
    {
        $html = '';
        if (empty($options)) {
            $options = $this->getOptionsByName($name);
        }
        
        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }

            if (!isset($option['value'])) {
                continue;
            }

            $checked = isset($option['checked']) ? (boolean) $option['checked'] : false;
            if ($option['value'] === $value) {
                $checked = true;
            }

            $html .= $this->getHtml()->radio($name, $option['value'], $checked) . $option['label'];
        }

        return $html;
    }

    /**
     * 获取表单元素：<input type="checkbox" /><input type="checkbox" /><input type="checkbox" />
     * @param string $name
     * @param array $value
     * @param array $attributes
     * @param array $options
     * @return string
     */
    public function checkbox($name, $value, $attributes, array $options = array())
    {
        $html = '';
        if (empty($options)) {
            $options = $this->getOptionsByName($name);
        }
        
        $name .= '[]';
        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }

            if (!isset($option['value'])) {
                continue;
            }

            $checked = isset($option['checked']) ? (boolean) $option['checked'] : false;
            if (is_array($value) && in_array($option['value'], $value)) {
                $checked = true;
            }

            $html .= $this->getHtml()->checkbox($name, $option['value'], $checked) . $option['label'];
        }

        return $html;
    }

    /**
     * 获取页面辅助类
     * @return instance of idea\mvc\Html
     */
    public function getHtml()
    {
        if ($this->_html === null) {
            $this->_html = new Html();
        }

        return $this->_html;
    }

    /**
     * 获取本表单类对应的服务类
     * @return instance of idea\saf\sl\Service
     */
    public function getService()
    {
        $className = str_replace('form', 'service', get_class($this));
        return Singleton::getInstance($className);
    }
}
